/*
Copyright (c) 2010, Daniele Simonetti
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "playlist.h"
#include "controller.h"
#include "utility.h"
#include "tagdialog.h"

#include "widgets/confirmdialog.h"

#include "player/playlistentry.h"
#include "player/playlistfactory.h"

#include "feats/thememanager.h"
#include "feats/movewithmouse.h"

#include <QFileDialog>
#include <QMenu>
#include <QContextMenuEvent>
#include <QTextStream>
#include <QInputDialog>
#include <QClipboard>
#include <QMessageBox>
#include <QActionGroup>
#include <QRegion>
#include <QPainter>
#include <QPainterPath>
#include <QSettings>
#include <QtConcurrentRun>
#include <QTimer>
#include <QTime>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    _mctx(NULL), _msort(NULL), _mthemes(NULL), _mmode(NULL),
    _mvisual(NULL), _mpls(NULL), _aplay(NULL), _agthemes(NULL),
    _agsortOrder(NULL), _plModel(NULL), _timerSB(NULL)
{
    ui->setupUi(this);

    // Crea il modello della playlist, sarebbe meglio creare un PlaylistController
    // e spostare questo codice li... pero' mi fa fatica
    _plModel = new PlayList(this);
    ui->listView->setModel(_plModel);

    _timerSB = new QTimer(this);
    _timerSB->start(2000);

    // Inizializza tutte le QAction che poi andranno dentro il ContextMenu
    initializeActions();

    //QCoreApplication* app = QApplication::instance();

    // Evento di cambio volume
    connect(ui->dialVolume, SIGNAL(valueChanged(int)), SIGNAL(volumeChange(int)));
    // Evento di progresso nel brano
    connect(ui->slProgress, SIGNAL(valueChanged(int)), SIGNAL(positionChange(int)));
    // Evento di click sulla X in alto a destra ( esci )
    connect(ui->btExit, SIGNAL(clicked()), this, SLOT(close()));
    // Evento di click sulla freccina in alto a destra ( minimizza )
    connect(ui->btMin, SIGNAL(clicked()), SLOT(showMinimized()));
    // Evento di doppioclick su un brano nella playlist ( play )
    connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(playTriggered(QModelIndex)));
    // Evento timer per aggiornare la status bar
    connect(_timerSB, SIGNAL(timeout()), SLOT(updateStatusBar()));
    // Evento di ricerca
    connect(ui->listView, SIGNAL(searchrequest(QString)), SLOT(searchItem(QString)));
}

MainWindow::~MainWindow()
{
    cleanupActions(_msort);
    delete _msort;
    _msort      = NULL;
    cleanupActions(_mthemes);
    delete _mthemes;
    _mthemes    = NULL;
    cleanupActions(_mmode);
    delete _mmode;
    _mmode      = NULL;
    cleanupActions(_mvisual);
    delete _mvisual;
    _mvisual    = NULL;
    cleanupActions(_mpls);
    delete _mpls;
    _mpls       = NULL;
    cleanupActions(_mctx);
    delete _mctx;
    _mctx       = NULL;

    delete _agthemes;
    delete _agsortOrder;

    delete ui;
    delete _plModel;
}

void MainWindow::setWindowTitle(const QString & title)
{
    QMainWindow::setWindowTitle(title);
    ui->lbTitle->setText(title);
}

void MainWindow::cleanupActions(QMenu* menu)
{
    if ( menu == NULL || !menu->actions().count() )
        return;

    foreach(QAction* act, menu->actions())
    {
        if ( act != NULL )
        {
            this->removeAction(act);
            delete act;
            act = NULL;
        }
    }
}

void MainWindow::initializeActions()
{
    _mctx = new QMenu(this);

    // About
    QAction* about = new QAction(tr("About uPlayer"), this);

    // Exit
    QAction* exit  = new QAction(tr("E&xit"), this);
    exit->setShortcut(QKeySequence(tr("CTRL+X", "Application Exit")));
    exit->setShortcutContext(Qt::ApplicationShortcut);

    // Play / Pause
    _aplay  = new QAction(tr("Play"), this);
    _aplay->setShortcut(QKeySequence(tr("ALT+X", "Play")));
    _aplay->setShortcutContext(Qt::ApplicationShortcut);

    // Stop
    QAction* stop  = new QAction(tr("Stop"), this);
    stop->setShortcut(QKeySequence(tr("ALT+V", "Stop")));
    stop->setShortcutContext(Qt::ApplicationShortcut);

    // Next Track
    QAction* next  = new QAction(tr("Next"), this);
    next->setShortcut(QKeySequence(tr("ALT+B", "Next Track")));
    next->setShortcutContext(Qt::ApplicationShortcut);

    // Previous Track
    QAction* prev  = new QAction(tr("Previous"), this);
    prev->setShortcut(QKeySequence(tr("ALT+Z", "Previous Track")));
    prev->setShortcutContext(Qt::ApplicationShortcut);

    // Add Tracks
    QAction* add = new QAction(tr("Add..."), this);
    add->setShortcut(QKeySequence(tr("CTRL+O", "Add Tracks to Playlist")));

    QAction* addurl = new QAction(tr("Add URI..."), this);
    addurl->setShortcut(QKeySequence(tr("CTRL+L", "Add Network stream")));

    // Remove Tracks
    QAction* rem = new QAction(tr("Remove"), this);
    rem->setShortcut(QKeySequence(tr("DEL", "Remove selected tracks from Playlist")));
    rem->setShortcutContext(Qt::ApplicationShortcut);

    // Erase File from Disk
    QAction* erase = new QAction(tr("Erase from disk"), this);
    erase->setShortcut(QKeySequence(tr("SHIFT+DEL", "Erase selected file from the filesystem")));
    erase->setShortcutContext(Qt::ApplicationShortcut);


    // Select All
    QAction* selall = new QAction(tr("Select All"), this);
    selall->setShortcut(QKeySequence(tr("CTRL+A", "Select all")));
    selall->setShortcutContext(Qt::ApplicationShortcut);

    // Select None
    QAction* selnone = new QAction(tr("Select None"), this);
    selnone->setShortcut(QKeySequence(tr("SHIFT+CTRL+A", "Select none")));
    selnone->setShortcutContext(Qt::ApplicationShortcut);

    // Edit Tags
    _atag = new QAction(tr("Edit Tags"), this);

    _mctx->addAction( _aplay   );
    _mctx->addAction( stop     );
    _mctx->addAction( next     );
    _mctx->addAction( prev     );
    _mctx->addSeparator();
    _mctx->addAction( add      );
    _mctx->addAction( addurl   );
    _mctx->addAction( rem      );
    _mctx->addAction( erase    );
    _mctx->addSeparator();
    _mctx->addAction( selall   );
    _mctx->addAction( selnone  );
    _mctx->addAction( _atag    );
    _mctx->addSeparator();

    // Le azioni con le scorciatoie da tastiera che devono essere utilizzabili sempre
    // le devo aggiungere anche alla mainView.
    addAction( _aplay   );
    addAction( stop     );
    addAction( next     );
    addAction( prev     );
    addAction( add      );
    addAction( addurl   );
    addAction( rem      );
    addAction( erase    );
    addAction( selall   );
    addAction( selnone  );
    addAction( exit     );

    // Menu' di ordinamento
    _msort = _mctx->addMenu(tr("Sort"));

    QAction* byfname = new QAction(tr("By filename"), this);
    QAction* bytitle = new QAction(tr("By caption"), this);
    QAction* byrand = new QAction(tr("Shuffle"), this);

    _msort->addAction(byfname);
    _msort->addAction(bytitle);
    _msort->addAction(byrand);
    _msort->addSeparator();
    _agsortOrder = new QActionGroup(this);
    QAction* asc = _agsortOrder->addAction(tr("Ascending"));
    asc->setCheckable(true);
    asc->setData(Qt::AscendingOrder);
    QAction* desc = _agsortOrder->addAction(tr("Descending"));
    desc->setCheckable(true);
    desc->setData(Qt::DescendingOrder);
    Qt::SortOrder sort_order = (Qt::SortOrder)Controller::settings()->value("sort_order", 0).toInt();
    if ( sort_order == Qt::AscendingOrder )
        asc->setChecked(true);
    else
        desc->setChecked(true);
    _plModel->setSortOrder(sort_order);
    _msort->addActions(_agsortOrder->actions());

    // Menu' di modo playback
    _mmode = _mctx->addMenu(tr("Playback mode"));
    _agplaymode = new QActionGroup(this);
    _plModel->setPlayMode((PlaybackModes)Controller::settings()->value("play_mode", 0).toInt());
    QAction* mshuffle   = _agplaymode->addAction(tr("Shuffle"));
    mshuffle->setCheckable(true);
    mshuffle->setData(Shuffle);
    QAction* mrepeatone = _agplaymode->addAction(tr("Repeat One Song"));
    mrepeatone->setCheckable(true);
    mrepeatone->setData(RepeatOne);
    QAction* mrepeatall = _agplaymode->addAction(tr("Repeat"));
    mrepeatall->setCheckable(true);
    mrepeatall->setData(RepeatAll);
    QAction* mnorepeat  = _agplaymode->addAction(tr("No Repeat"));
    mnorepeat->setCheckable(true);
    mnorepeat->setData(RepeatAll);
    switch(_plModel->playMode())
    {
    case Shuffle:
        mshuffle->setChecked(true);
        break;
    case RepeatOne:
        mrepeatone->setChecked(true);
        break;
    case RepeatAll:
        mrepeatall->setChecked(true);
    case NoRepeat:
    default:
        mnorepeat->setChecked(true);
        break;
    };
    _mmode->addActions(_agplaymode->actions());

    // Menu' temi

    // applico l'ultimo tema selezionato, e poi lo checko
    QString last_theme = Controller::settings()->value("last_theme", "default").toString();
    ThemeManager::applyTheme(last_theme, this);

    _mthemes  = _mctx->addMenu(tr("Themes"));
    _agthemes = new QActionGroup(this);

    foreach(QString theme, ThemeManager::getThemes())
    {
        QAction* a = _agthemes->addAction(theme);
        a->setCheckable(true);
        if ( a->text() == last_theme )
            a->setChecked(true);
    }

    _mthemes->addActions(_agthemes->actions());

    // Menu Playlist
    _mpls = _mctx->addMenu(tr("Playlist"));
    QAction* newPls = new QAction(tr("Clear"), this);
    QAction* loadPls = new QAction(tr("Load..."), this);
    QAction* savePls = new QAction(tr("Save"), this);
    QAction* saveAsPls = new QAction(tr("Save As..."), this);
    _mpls->addAction(newPls);
    _mpls->addAction(loadPls);
    _mpls->addAction(savePls);
    _mpls->addAction(saveAsPls);

    _mctx->addSeparator();
    _mctx->addAction( about    );

    connect(about, SIGNAL(triggered()), SLOT(showAboutBox()));
    connect(_aplay, SIGNAL(triggered()), SLOT(playTriggered()));

    connect(stop, SIGNAL(triggered()), SIGNAL(streamStop()));
    connect(next, SIGNAL(triggered()), SLOT(requestNext()));
    connect(prev, SIGNAL(triggered()), SLOT(requestPrev()));

    connect(add,      SIGNAL(triggered()), SLOT(addMusic()));
    connect(addurl,   SIGNAL(triggered()), SLOT(addURI()));
    connect(rem,      SIGNAL(triggered()), SLOT(removeSelected()));
    connect(erase,      SIGNAL(triggered()), SLOT(eraseSelected()));
    connect(selall,   SIGNAL(triggered()), SLOT(selectAll()));
    connect(selnone,  SIGNAL(triggered()), SLOT(selectNone()));

    connect(_atag,  SIGNAL(triggered()), SLOT(showTagEditor()));

    connect(newPls,    SIGNAL(triggered()), _plModel,   SLOT(clear()));
    connect(loadPls,    SIGNAL(triggered()),            SLOT(openPlaylist()));
    connect(savePls,    SIGNAL(triggered()),            SLOT(savePlaylist()));
    connect(saveAsPls,  SIGNAL(triggered()),            SLOT(savePlaylistAs()));


    connect(byfname,   SIGNAL(triggered()), _plModel,   SLOT(sortByFilename()));
    connect(bytitle,   SIGNAL(triggered()), _plModel,   SLOT(sortByTitle()));
    connect(byrand,    SIGNAL(triggered()), _plModel,   SLOT(shuffle()));

    connect(_mthemes,       SIGNAL(triggered(QAction*)),    SLOT(applyTheme(QAction*)));
    connect(_agsortOrder,   SIGNAL(triggered(QAction*)),    SLOT(setSortOrder(QAction*)));
    connect(_agplaymode,    SIGNAL(triggered(QAction*)),    SLOT(setPlayMode(QAction*)));
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type())
    {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void MainWindow::closeEvent(QCloseEvent * e)
{
    /* Salva l'ultima posizione */
    Controller::settings()->setValue("win_pos", pos());
    Controller::settings()->setValue("win_geom", geometry());

    e->accept();
}

void MainWindow::contextMenuEvent(QContextMenuEvent *e)
{
    e->accept();

    PlaylistEntry* hover = NULL;
    if ( ui->listView->selectionModel()->currentIndex().isValid() )
        hover = _plModel->at(ui->listView->selectionModel()->currentIndex());

    if ( hover )
    {
        _aplay->setEnabled(true);

        if ( hover->status() == PlaylistEntry::Playing )
            _aplay->setText(tr("Pause"));
        else if ( hover->status() == PlaylistEntry::Paused )
            _aplay->setText(tr("Resume"));
        else
            _aplay->setText(tr("Play"));

        if ( !hover->isNetwork() )
        {
            _atag->setVisible(true);
            if ( hover->readonly() )
                _atag->setText(tr("View Tags"));
            else
                _atag->setText(tr("Edit Tags"));
        }
    }
    else
    {
        _aplay->setEnabled(false);        
        _atag->setVisible(false);
    }

    //_mctx->popup(e->globalPos());
    _mctx->exec(e->globalPos());
}

void MainWindow::mousePressEvent(QMouseEvent* e)
{
    emit mousePressed(e);
}

void MainWindow::mouseReleaseEvent(QMouseEvent* e)
{
    emit mouseReleased(e);
}

void MainWindow::mouseMoveEvent(QMouseEvent* e)
{
    emit mouseMove(e);
}

void MainWindow::playTriggered()
{
    if ( ui->listView->selectionModel()->currentIndex().isValid() )
    {
        QModelIndex index = ui->listView->selectionModel()->currentIndex();
        playTriggered(index);
    }
}

void MainWindow::playTriggered(QModelIndex index)
{
    _plModel->setCurrentIndex(index);
    PlaylistEntry* myStream = _plModel->current();
    emit streamPlay(index.row(), myStream);
}

void MainWindow::applyTheme(QAction* act)
{
    ThemeManager::applyTheme(act->text(), this);
    act->setChecked(true);

    Controller::settings()->setValue("last_theme", act->text());
}

void MainWindow::setSortOrder(QAction* act)
{
    Qt::SortOrder order = (Qt::SortOrder)act->data().toInt();
    act->setChecked(true);
    Controller::settings()->setValue("sort_order", order);
    _plModel->setSortOrder(order);

}

void MainWindow::setPlayMode(QAction* act)
{
    PlaybackModes mode = (PlaybackModes)act->data().toInt();
    act->setChecked(true);
    Controller::settings()->setValue("play_mode", mode);
    _plModel->setPlayMode(mode);
}

void MainWindow::selectAll()
{
    ui->listView->selectAll();
}

void MainWindow::selectNone()
{
    ui->listView->selectionModel()->clear();
}

void MainWindow::removeSelected()
{
    while( ui->listView->selectionModel()->selectedIndexes().count() )
    {
        _plModel->removeRow(ui->listView->selectionModel()->selectedIndexes().at(0).row());
    }
}

void MainWindow::eraseSelected()
{
    bool askConfirm = Controller::settings()->value("confirm_erase", true).toBool();
    bool erase = true;
    bool error = false;
    if ( askConfirm )
    {
        // Ask for confirmation before continuing
        ConfirmDialog dlg;
        dlg.setQuestion(tr("This will erase the selected file(s) PERMANENTLY from your disk, are you sure?"));
        dlg.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No);

        erase = dlg.exec() == QDialog::Accepted;

        if (dlg.showAgain())
            Controller::settings()->setValue("confirm_erase", false);
    }

    if ( erase )
    {
        while( ui->listView->selectionModel()->selectedIndexes().count() )
        {
            PlaylistEntry* e = _plModel->at(ui->listView->selectionModel()->selectedIndexes().at(0));
            if ( e && QFile::remove(e->fullPath()))
                _plModel->removeRow(ui->listView->selectionModel()->selectedIndexes().at(0).row());
            else
                error = true;
        }
    }
}

void MainWindow::addMusic()
{
    QString last_dir = Controller::settings()->value("last_dir", QDir::homePath()).toString();
    QStringList files = QFileDialog::getOpenFileNames(
                            this,
                            tr("Add music"),
                            last_dir,
                            tr("Audio Files (*.wav *.aif *.mp3 *.mp2 *.mp1, *.ogg);;All files (*.*)"));

    if (files.count())
    {
        // workaround, questa cosa la dovrebbe fare il motore QT, non io
        Controller::settings()->setValue("last_dir", QFileInfo(files.at(0)).absolutePath());
        QtConcurrent::run(_plModel, &PlayList::addFiles, files, QDir(), -1);
        //_plModel->addFiles(files);
    }
}

void MainWindow::addURI()
{
    QString def = "http://";

    QClipboard *clipboard = QApplication::clipboard();
    if ( clipboard )
    {
        QUrl clip = QUrl::fromUserInput(clipboard->text());
        if ( clip.isValid() )
            def = clip.toString();
    }

    bool ok;
    QString text = QInputDialog::getText(this, tr("Add network stream"),
                                         tr("Type the address to stream (e.g. http://mystreamingsite.com/music)"), QLineEdit::Normal,
                                         def, &ok);
    if (ok && !text.isEmpty())
        _plModel->addURI(QUrl::fromUserInput(text));
}

void MainWindow::openPlaylist()
{
    QString last_dir = Controller::settings()->value("last_dir", QDir::homePath()).toString();
    QString file = QFileDialog::getOpenFileName(
                       this,
                       tr("Load playlist"),
                       last_dir,
                       tr("Playlist Files (*.m3u *.pls);;All files (*.*)"));

    IPlaylistReader* r = PlaylistFactory::createReader(file);

    // workaround, questa cosa la dovrebbe fare il motore QT, non io
    if ( !file.isNull() && !file.isEmpty() )
        Controller::settings()->setValue("last_dir", QFileInfo(file).absolutePath());
    if (r)
        _plModel->addPlaylist(r);
}

void MainWindow::savePlaylist()
{
    if ( !_plModel->saveOnDisk() )
        savePlaylistAs();
}

void MainWindow::savePlaylistAs()
{
    QString last_dir = Controller::settings()->value("last_dir", QDir::homePath()).toString();
    QStringList filters;
    filters << tr("M3U Playlist Format (*.m3u)")
            << tr("PLS Playlist Format (*.pls)");

    QString the_filter = filters[0];
    QString file = QFileDialog::getSaveFileName(
                       this,
                       tr("Save playlist As"),
                       last_dir,
                       filters.join(";;"),
                       &the_filter);

    if ( !file.isNull() && !file.isEmpty() )
    {
        IPlaylistWriter* w = NULL;
        if ( the_filter == filters[0] )
        {
            util::file_append_extension(&file, ".m3u");
            w = new M3UPlaylistWriter(file);
        }
        else if ( the_filter == filters[1] )
        {
            util::file_append_extension(&file, ".pls");
            w = new PlsPlaylistWriter(file);
        }

        // workaround, questa cosa la dovrebbe fare il motore QT, non io
        Controller::settings()->setValue("last_dir", QFileInfo(file).absolutePath());
        _plModel->saveOnDisk(w);
    }
}

void MainWindow::setVolume(int value)
{
    ui->dialVolume->setValue(value);
}

void MainWindow::setStreamLen(int value)
{
    ui->slProgress->setMaximum(value);
}

void MainWindow::setStreamPos(int value)
{
    ui->slProgress->blockSignals(true);
    ui->slProgress->setValue(value);
    ui->slProgress->blockSignals(false);

    QTime time;
    if ( Controller::settings()->value("show_elapsed", false).toBool() )
        time = QTime().addSecs(value);
    else
    {
        PlaylistEntry* e = _plModel->current();
        time = QTime().addSecs(e->len()-value);
    }
    //double dvalue = ((double)time.second()/100) + time.minute();
    ui->lcdNumber->display(time.toString("m:ss"));
}

void MainWindow::requestNext()
{
    PlaylistEntry* myStream = _plModel->next();
    if ( myStream )
    {
        ui->listView->setCurrentIndex(_plModel->currentIndex());
        ui->listView->selectionModel()->select(_plModel->currentIndex(), QItemSelectionModel::ClearAndSelect);
        ui->listView->scrollTo(_plModel->currentIndex(), QAbstractItemView::PositionAtCenter);
        emit streamPlay(0, myStream);
    }
    else
        this->statusBar()->showMessage(tr("Nothing to play..."));
}

void MainWindow::requestPrev()
{
    PlaylistEntry* myStream = _plModel->prev();
    if ( myStream )
    {
        ui->listView->setCurrentIndex(_plModel->currentIndex());
        ui->listView->selectionModel()->select(_plModel->currentIndex(), QItemSelectionModel::ClearAndSelect);
        ui->listView->scrollTo(_plModel->currentIndex(), QAbstractItemView::PositionAtCenter);
        emit streamPlay(0, myStream);
    }
    else
        this->statusBar()->showMessage(tr("Nothing to play..."));
}

void MainWindow::stopped()
{
    this->statusBar()->showMessage(tr("Stopped."));
}

void MainWindow::paused()
{
    PlaylistEntry* entry = _plModel->current();
    if ( entry )
    {
        QString result;
        QTextStream(&result) << tr("Paused: ") << entry->toString();
        //this->statusBar()->showMessage(result);
    }
}

void MainWindow::playing()
{
    PlaylistEntry* entry = _plModel->current();
    if ( entry )
    {
        QString result;
        QTextStream(&result) << tr("Now playing: ") << entry->toString();
        //this->statusBar()->showMessage(result);
    }
}

void MainWindow::showAboutBox()
{
    QMessageBox::about(this,tr("uPlayer"), tr("uPlayer does only what you need."));
}

PlayList* MainWindow::playList() const
{
    return _plModel;
}

void MainWindow::paintEvent(QPaintEvent *e)
{
    Q_UNUSED(e);

    qreal opacity = Controller::settings()->value("view_opacity", "0.8").toDouble();
    int roundness = Controller::settings()->value("view_roundness", "8").toInt();

    QRect widget_rect = this->rect();
    widget_rect.adjust(-1,-1,+1,+1);

    QPainter painter(this);
    QPen pen = Qt::gray;
    pen.setCosmetic(true);

    painter.save();
    //painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(pen);


    // clip
    QPainterPath rounded_rect;
    //rounded_rect.addRoundRect(1, 1, widget_rect.width() - 2, widget_rect.height() - 2, roundness, roundness);
    rounded_rect.addRoundedRect(widget_rect, roundness, roundness, Qt::AbsoluteSize);
    painter.setClipPath(rounded_rect);

    // get clipping region
    QRegion maskregion = painter.clipRegion();

    // mask the widget
    setMask(maskregion);
    painter.setOpacity(opacity);

    // border
    //painter.drawPath(rounded_rect);

    // restore painter
    painter.restore();
}

void MainWindow::showTagEditor()
{    
    /*if ( _mctx->aboutToHide() )
    QTimer timer(this);
    connect(&timer, SIGNAL(timeout()), SLOT(showTagEditor()));
    timer.start();;*/

    PlaylistEntry* hover = NULL;
    if ( ui->listView->selectionModel()->currentIndex().isValid() )
    {
        hover = _plModel->at(ui->listView->selectionModel()->currentIndex());
        showTagEditor(hover);

    }
}

void MainWindow::showTagEditor(PlaylistEntry *entry)
{
    if ( !entry )
        return;

    TagDialog td;
    td.setWindowFlags(Qt::FramelessWindowHint);
    td.setReadOnly(entry->readonly());    
    td.setWindowModality(Qt::ApplicationModal);
    td.setFile(entry->fullPath());
    const int spacing = 6;
    int tdw = td.width();
    int tdh = td.height();
    int tdx = x();
    int tdy = y();
    if ( x() > (tdw+spacing) )
        tdx = x() - tdw - spacing;
    else
        tdx = x() + tdw + spacing;
    if (height() > tdh)
        tdy = (height()-tdh)/2 + y();
    else
        tdy = y()+(height()-tdh)/2;

    td.move(tdx, tdy);
    if ( td.exec() == QDialog::Accepted )
    {
        entry->updateTags();
        QModelIndex i = _plModel->indexOf(entry);
        if ( i.isValid() )
            ui->listView->update(i);
    }
}

void MainWindow::updateStatusBar()
{
    static int display = 0;
    PlaylistEntry* entry = _plModel->current();    
    if ( entry && (entry->status() == PlaylistEntry::Playing || entry->status() == PlaylistEntry::Paused) )
    {
        QString result;
        QTextStream s(&result);

        if ( entry->isNetwork() )
            entry->updateTags();

        switch(display)
        {
        case 0:
            s << tr("Title: ") << entry->title();
            break;
        case 1:
            s << tr("Artist: ") << entry->artist();
            break;
        case 2:
            s << tr("Album: ") << entry->album();
            break;
        case 3:
            s << tr("Genre: ") << entry->genre();
            break;
        case 4:
            if ( entry->isNetwork() )
                s << tr("Url: ") << entry->url().toString();
            else
                s << entry->fileName();
            break;
        }
        this->statusBar()->showMessage(result, 2000);
        display = (display+1) % 5;
    }
    else
        display = 0;
}

void MainWindow::searchItem(const QString& search)
{
    statusBar()->showMessage(search, 1500);
    QList<QModelIndex> matches;
    QModelIndex idx = _plModel->search(search, matches, ui->listView->currentIndex());

    if ( idx.isValid() )
    {
        ui->listView->setCurrentIndex(idx);
        ui->listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
        ui->listView->scrollTo(idx, QAbstractItemView::PositionAtCenter);
    }
}
